use crate::ast::*;
use crate::error::{CompilerError, Result};
use crate::llvm_backend::LLVMBackend;
use inkwell::values::BasicValueEnum;

impl<'ctx> LLVMBackend<'ctx> {


    /// Generate and_then() method - chains Result operations
    /// If Ok, calls f(value) and returns the Result it returns; if Err, keeps Err
    pub(super) fn generate_result_and_then(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.and_then() expects 1 argument (function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "and_then_check",
        )?;

        // Create blocks: ok_block (call function), err_block (keep Err)
        let ok_block = self.context.append_basic_block(func, "and_then_ok");
        let err_block = self.context.append_basic_block(func, "and_then_err");
        let continue_block = self.context.append_basic_block(func, "and_then_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: extract Ok value, call function, return the Result it returns
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;

        // Store ok_value in a temporary variable so we can use it in function call
        let ok_type = self.nrc_type_to_llvm_type(&Some(*result_type.ok_type.clone()))?;
        let ok_temp_alloca = self.builder.build_alloca(ok_type, "ok_temp")?;
        self.builder.build_store(ok_temp_alloca, ok_value)?;

        // Create a temporary variable name for the ok value
        let ok_temp_var = format!("__and_then_ok_{}", location.line);

        // Store the alloca in variable_map temporarily
        let old_ok_value = self
            .variable_map
            .insert(ok_temp_var.clone(), ok_temp_alloca.into());
        let old_ok_type = self
            .variable_type_map
            .insert(ok_temp_var.clone(), *result_type.ok_type.clone());

        // Create a call expression: f(ok_temp_var)
        let func_expr = arguments[0].clone();
        let ok_var_expr = Expression::Variable(ok_temp_var.clone());
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(func_expr),
            arguments: vec![ok_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });

        // Generate the function call - it returns a Result
        let new_result_value = self.generate_expression(&call_expr)?;

        // Infer the new Result type from the function return type
        let new_result_type = match self.infer_expression_type(&call_expr) {
            Some(Type::Result(new_rt)) => new_rt,
            _ => result_type.clone(), // Fallback to current result_type
        };

        // Restore variable map
        if let Some(old) = old_ok_value {
            self.variable_map.insert(ok_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&ok_temp_var);
        }
        if let Some(old) = old_ok_type {
            self.variable_type_map.insert(ok_temp_var, old);
        } else {
            self.variable_type_map.remove(&ok_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: create new Result::Err with same error value
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;
        let new_err_result =
            self.create_result_value(err_value, &*result_type.err_type, result_type, "Err")?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either the function's Result or the Err Result
        self.builder.position_at_end(continue_block);
        let result_llvm_type = self.get_result_llvm_type(&new_result_type)?;
        let phi = self
            .builder
            .build_phi(result_llvm_type, "and_then_result")?;
        phi.add_incoming(&[(&new_result_value, ok_block), (&new_err_result, err_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate or_else() method - chains Result operations with error handling
    /// If Err, calls f(error) and returns the Result it returns; if Ok, keeps Ok
    pub(super) fn generate_result_or_else(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.or_else() expects 1 argument (function), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            tag,
            zero,
            "or_else_check",
        )?;

        // Create blocks: ok_block (keep Ok), err_block (call function)
        let ok_block = self.context.append_basic_block(func, "or_else_ok");
        let err_block = self.context.append_basic_block(func, "or_else_err");
        let continue_block = self.context.append_basic_block(func, "or_else_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: create new Result::Ok with same Ok value
        self.builder.position_at_end(ok_block);
        let ok_value = self.extract_result_ok_value(result_value, result_type)?;
        let new_ok_result =
            self.create_result_value(ok_value, &*result_type.ok_type, result_type, "Ok")?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: extract Err value, call function, return the Result it returns
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;

        // Store err_value in a temporary variable so we can use it in function call
        let err_type = self.nrc_type_to_llvm_type(&Some(*result_type.err_type.clone()))?;
        let err_temp_alloca = self.builder.build_alloca(err_type, "err_temp")?;
        self.builder.build_store(err_temp_alloca, err_value)?;

        // Create a temporary variable name for the err value
        let err_temp_var = format!("__or_else_err_{}", location.line);

        // Store the alloca in variable_map temporarily
        let old_err_value = self
            .variable_map
            .insert(err_temp_var.clone(), err_temp_alloca.into());
        let old_err_type = self
            .variable_type_map
            .insert(err_temp_var.clone(), *result_type.err_type.clone());

        // Create a call expression: f(err_temp_var)
        let func_expr = arguments[0].clone();
        let err_var_expr = Expression::Variable(err_temp_var.clone());
        let call_expr = Expression::Call(CallExpr {
            callee: Box::new(func_expr),
            arguments: vec![err_var_expr],
            type_args: Vec::new(),
            resolved: None,
            location: *location,
        });

        // Generate the function call - it returns a Result
        let new_result_value = self.generate_expression(&call_expr)?;

        // Infer the new Result type from the function return type
        let new_result_type = match self.infer_expression_type(&call_expr) {
            Some(Type::Result(new_rt)) => new_rt,
            _ => result_type.clone(), // Fallback to current result_type
        };

        // Restore variable map
        if let Some(old) = old_err_value {
            self.variable_map.insert(err_temp_var.clone(), old);
        } else {
            self.variable_map.remove(&err_temp_var);
        }
        if let Some(old) = old_err_type {
            self.variable_type_map.insert(err_temp_var, old);
        } else {
            self.variable_type_map.remove(&err_temp_var);
        }

        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either the Ok Result or the function's Result
        self.builder.position_at_end(continue_block);
        let result_llvm_type = self.get_result_llvm_type(&new_result_type)?;
        let phi = self.builder.build_phi(result_llvm_type, "or_else_result")?;
        phi.add_incoming(&[(&new_ok_result, ok_block), (&new_result_value, err_block)]);
        Ok(phi.as_basic_value())
    }


    /// Generate and() method - combines two Results
    /// If both are Ok, returns other; otherwise returns first Err
    pub(super) fn generate_result_and(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.and() expects 1 argument (other Result), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Generate the other Result value
        let other_result_value = self.generate_expression(&arguments[0])?;

        // Infer the other Result type
        let other_result_type = match self.infer_expression_type(&arguments[0]) {
            Some(Type::Result(other_rt)) => other_rt,
            _ => result_type.clone(), // Fallback to current result_type
        };

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "and_check")?;

        // Create blocks: ok_block (check other), err_block (return first Err)
        let ok_block = self.context.append_basic_block(func, "and_ok");
        let err_block = self.context.append_basic_block(func, "and_err");
        let continue_block = self.context.append_basic_block(func, "and_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: check if other is also Ok
        self.builder.position_at_end(ok_block);
        let other_tag = self.extract_result_tag(other_result_value)?;
        let other_is_ok = self.builder.build_int_compare(
            inkwell::IntPredicate::EQ,
            other_tag,
            zero,
            "other_is_ok",
        )?;

        let both_ok_block = self.context.append_basic_block(func, "and_both_ok");
        let other_err_block = self.context.append_basic_block(func, "and_other_err");

        self.builder
            .build_conditional_branch(other_is_ok, both_ok_block, other_err_block)?;

        // Both Ok: return other
        self.builder.position_at_end(both_ok_block);
        self.builder.build_unconditional_branch(continue_block)?;

        // Other is Err: return other (the Err)
        self.builder.position_at_end(other_err_block);
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: return first Err (current Result's Err)
        self.builder.position_at_end(err_block);
        let err_value = self.extract_result_err_value(result_value, result_type)?;
        let err_result =
            self.create_result_value(err_value, &*result_type.err_type, result_type, "Err")?;
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either other Result or first Err
        self.builder.position_at_end(continue_block);
        let result_llvm_type = self.get_result_llvm_type(&other_result_type)?;
        let phi = self.builder.build_phi(result_llvm_type, "and_result")?;
        phi.add_incoming(&[
            (&other_result_value, both_ok_block),
            (&other_result_value, other_err_block),
            (&err_result, err_block),
        ]);
        Ok(phi.as_basic_value())
    }


    /// Generate or() method - combines two Results
    /// If first is Ok, returns first; otherwise returns other
    pub(super) fn generate_result_or(
        &mut self,
        result_value: BasicValueEnum<'ctx>,
        result_type: &crate::ast::types::ResultType,
        arguments: &[Expression],
        location: &crate::error::Location,
    ) -> Result<BasicValueEnum<'ctx>> {
        if arguments.len() != 1 {
            return Err(CompilerError::type_error(
                location.line,
                location.column,
                &format!(
                    "Result.or() expects 1 argument (other Result), got {}",
                    arguments.len()
                ),
            ));
        }

        let func = self
            .current_function
            .ok_or_else(|| CompilerError::internal("Cannot create blocks outside function"))?;

        // Generate the other Result value
        let other_result_value = self.generate_expression(&arguments[0])?;

        // Infer the other Result type
        let other_result_type = match self.infer_expression_type(&arguments[0]) {
            Some(Type::Result(other_rt)) => other_rt,
            _ => result_type.clone(), // Fallback to current result_type
        };

        let tag = self.extract_result_tag(result_value)?;
        let zero = self.context.i32_type().const_int(0, false);
        let is_ok =
            self.builder
                .build_int_compare(inkwell::IntPredicate::EQ, tag, zero, "or_check")?;

        // Create blocks: ok_block (return first), err_block (return other)
        let ok_block = self.context.append_basic_block(func, "or_ok");
        let err_block = self.context.append_basic_block(func, "or_err");
        let continue_block = self.context.append_basic_block(func, "or_continue");

        self.builder
            .build_conditional_branch(is_ok, ok_block, err_block)?;

        // Ok branch: return first Result (current)
        self.builder.position_at_end(ok_block);
        // For type compatibility, we may need to create a new Result with the correct type
        // But for now, we'll just return the current result_value
        self.builder.build_unconditional_branch(continue_block)?;

        // Err branch: return other Result
        self.builder.position_at_end(err_block);
        self.builder.build_unconditional_branch(continue_block)?;

        // Continue block: return either first Result or other Result
        self.builder.position_at_end(continue_block);
        let result_llvm_type = self.get_result_llvm_type(&other_result_type)?;
        let phi = self.builder.build_phi(result_llvm_type, "or_result")?;
        phi.add_incoming(&[(&result_value, ok_block), (&other_result_value, err_block)]);
        Ok(phi.as_basic_value())
    }

}
